#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
#include "stdio.h"
#include "math.h"
#include "LogicData.h"
#include "cocostudio/CocoStudio.h"
#include "ui/CocosGUI.h"
#include "UserDataManager.h"
#include "FailureLayer.h"
#include "WinnerLayer.h"

#include "ParticleSystemCacheUtil.h"
#include "SimpleAudioEngine.h"

USING_NS_CC;

Scene *scene;

Scene* HelloWorld::createScene()
{
	//使用物理世界创建场景
	scene = Scene::createWithPhysics();
	scene->addChild(HelloWorld::create());
	return  scene;
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
	printf("Error while loading: %s\n", filename);
	printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
	//////////////////////////////
	// 1. super init first
	if (!Layer::init())
	{
		return false;
	}
    
    m_fireFailure = false;
    // 数据初始化
    UserDataManager::getInstance()->battleData->resethData();

	//显示物理世界调试状态, 显示红色的框, 方便调试
	physicsWorld = scene->getPhysicsWorld();
	physicsWorld->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
	LogicData *lgd = LogicData::getInstance();
	physicsWorld->setGravity(lgd->gravity);

	m_size = Director::getInstance()->getWinSize();
	auto visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();
	m_centre = Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y);
	CCLOG("winsize=(%f,%f) vsize =(%f,%f), o = (%f,%f)",
		m_size.width, m_size.height,
		visibleSize.width, visibleSize.height, origin.x, origin.y);

	//游戏背景
	auto bg_sp = Sprite::create("imgs/bg.png");
	bg_sp->setPosition(m_centre);
	addChild(bg_sp);


	//轨迹层
	drawTraceLayer = DrawTraceLayer::create();
	this->addChild(drawTraceLayer);
    
    


    
	//触摸事件
	auto listener1 = EventListenerTouchOneByOne::create();
	// trigger when you push down
	listener1->onTouchBegan = [=](Touch* touch, Event* event) {
		if (lgd->status == 1 && lgd->gameStatus == 1) {
			auto liv_pos = touch->getLocationInView();
			liv_pos = Director::getInstance()->convertToUI(liv_pos);
			CCLOG("touch began %f,%f", liv_pos.x, liv_pos.y);
			float dist = liv_pos.distance(lgd->ballsposition_c);
			if (dist <= lgd->ballRadius) {
				lgd->status = 2;
				CCLOG("进入状态2，拉球中");
			}
		}
		return true; // if you are consuming it
	};
	listener1->onTouchMoved = [=](Touch* touch, Event* event) {
		if (lgd->status == 2 && lgd->gameStatus == 1) { //拉球并画轨迹
			auto tp = touch->getLocationInView();
			tp = Director::getInstance()->convertToUI(tp);
			actionBody->getNode()->setPosition(tp);
			Vec2 dis = tp - lgd->ballsposition_c;
			//垂直向上为0，正时针方向的角度
			float angle = Vec2(dis.y, dis.x).getAngle();
			//sp->setRotation( / 3.14f * 180);
			float dist = tp.getDistance(lgd->ballsposition_c);
			float lx = sin(angle)*dist; //lx = fabs(lx);
			float ly = cos(angle)*dist; //ly = fabs(ly);
			//CCLOG("%f,%f", lx, ly);
			//why ?????
			lgd->currentV.y = -ly;
			lgd->currentV.x = -lx;
			drawTraceLayer->refreshTraceView(tp, lgd->ballsposition_c, lgd->currentV, lgd->gravity, lgd->bounce);
		}
	};
	listener1->onTouchEnded = [=](Touch* touch, Event* event) {
		if (lgd->status == 2 && lgd->gameStatus == 1) {
            CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("audio/toulan.mp3");
            
			lgd->status = -1;
			actionBody->setDynamic(true);
			actionBody->setVelocity(lgd->currentV*lgd->bounce);
			drawTraceLayer->clearTrace();
			//计算球的最高度

			//末速度Vt＝Vo+at  bounce * currentV.y =lgd->gravity.y*dt
			//计算最高高度
			Vec2 currentV = lgd->currentV;
			float dt = (lgd->bounce * currentV.y / -lgd->gravity.y);
			if (dt <= 0) dt = 0;
			float dy = lgd->bounce * currentV.y*dt - (-lgd->gravity.y*dt*dt) / 2.0f;
			lgd->ballMaxHeight = lgd->ballsposition_c.y + dy;
			CCLOG("max height=%f dt=%f", lgd->ballMaxHeight, dt);

		}
	};
	listener1->onTouchCancelled = [=](Touch* touch, Event* event) {
		if (lgd->gameStatus == 1) {
			lgd->status = 1;
			actionBody->setDynamic(false);
			actionBody->getNode()->setPosition(lgd->ballsposition_c);
			drawTraceLayer->clearTrace();
		}
	};

	// Add listener
	_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);
	//END 触摸事件
    
    // 分数
    lab_score = Label::createWithTTF("0", "font/005.TTF", 75);
    lab_score->setAnchorPoint(Vec2(0, 1));
    lab_score->setPosition(Vec2(20, m_size.height-20));
    addChild(lab_score);
    
    // 初始化篮球跟篮板
    this->initBall();
    this->initBasket();

	//游戏进程回调
	NotificationCenter::getInstance()->addObserver(this, callfuncO_selector(HelloWorld::onFallIn), "FallIn", nullptr);
	NotificationCenter::getInstance()->addObserver(this, callfuncO_selector(HelloWorld::onOut), "Out", nullptr);

	return true;
}

void HelloWorld::initBall()
{
    //初始化球的位置和逻辑变量
//    Vec2 ballsposition = LogicData::getInstance()->ballsposition;
//    ballsposition = Vec2(m_centre.x - 100, m_centre.y);
    Vec2 ballsposition = Vec2(150, 410);
    LogicData::getInstance()->ballsposition = ballsposition;
    LogicData::getInstance()->ballsposition_c = ballsposition;
    
    //左边篮筐，位置和篮球的位置一样的
    ballBox = CSLoader::createNode("res/lankuan.csb");
    ballBox->setPosition(ballsposition);
    this->addChild(ballBox);
    
    //添加篮球
    //this->actionBody = PhysicsBody::createBox(Size(10.0f, 10.0f), PhysicsMaterial(0.1f, 1.0f, 0.0f));
    this->actionBody = PhysicsBody::createCircle(LogicData::getInstance()->ballRadius, PhysicsMaterial(1.0f, 0.3f, 1.0f));
    //actionBody->setRotationEnable(false);
    actionBody->setDynamic(false);
    actionBody->setGravityEnable(true);
    actionBody->setCategoryBitmask(LogicData::getInstance()->ball1Mask);
    actionBody->setCollisionBitmask(LogicData::getInstance()->wallMask | LogicData::getInstance()->ball1Mask | LogicData::getInstance()->kuangMask | LogicData::getInstance()->ropeMask);
    actionBody->setContactTestBitmask(LogicData::getInstance()->wallMask | LogicData::getInstance()->ball1Mask | LogicData::getInstance()->kuangMask | LogicData::getInstance()->ropeMask);
    
    //摩擦力
    actionBody->getFirstShape()->setFriction(0.01f);
    //set initial velocity of physicsBody
    actionBody->setAngularVelocity(2);
    ball = Sprite::create("imgs/ball.png");
    ball->setPosition(ballsposition);
    ball->setPhysicsBody(actionBody);
    addChild(ball);
    ball->setTag(LogicData::getInstance()->ballTag);
    actionBody->setTag(LogicData::getInstance()->ballTag);
}

void HelloWorld::initBasket()
{
    //篮板
    baskWallLayer = BaskWallLayer::create();
    baskWallLayer->physicsWorld = physicsWorld;
    baskWallLayer->setPosition(Vec2(480, 650));
    addChild(baskWallLayer);
    
//    baskWallLayer->runAction(RepeatForever::create(Sequence::create(
//                                                                    MoveBy::create(1, Vec2(0, 100)),
//                                                                    MoveBy::create(1, Vec2(0, -100)),
//                                                                    NULL)));
}

void HelloWorld::onEnter()
{
	Layer::onEnter();

	//添加监听器
	auto contactListener = EventListenerPhysicsContact::create();
	//设置监听器的碰撞开始函数
	contactListener->onContactBegin = CC_CALLBACK_1(HelloWorld::onContactBegin, this);
	//添加到事件分发器中
	_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);

	//解决刚体穿透问题
	scheduleOnce(schedule_selector(HelloWorld::updateStart), 2);

}

void HelloWorld::onExit()
{
    Layer::onExit();
    NotificationCenter::getInstance()->removeObserver(this, "FallIn");
    NotificationCenter::getInstance()->removeObserver(this, "Out");
}

void HelloWorld::updateStart(float dt)
{
	//重点在这里
	scene->getPhysicsWorld()->setAutoStep(false);
	scheduleUpdate();
}

void HelloWorld::update(float dt)
{
	// use fixed time and calculate 3 times per frame makes physics simulate more precisely.
	//这里表示先走3步瞧瞧 如果fps是1/60 三个setp就是1/180
	for (int i = 0; i < 3; ++i)

	{
		scene->getPhysicsWorld()->step(1 / 180.0f);

	}

	//检查球是否出界了，出了恢复状态
	Vec2 ballp = actionBody->getPosition();
	auto visibleSize = Director::getInstance()->getVisibleSize();
	float x = ballp.x;
	float y = ballp.y;
	if (x >= visibleSize.width || x <= 0 || y >= visibleSize.height || y <= 0) {
		NotificationCenter::getInstance()->postNotification("Out", this);
	}

}

bool HelloWorld::onContactBegin(const PhysicsContact& contact)
{

	LogicData *lgd = LogicData::getInstance();
	if (lgd->status != -1) {
		return true;
	}

	auto visibleSize = Director::getInstance()->getVisibleSize();
	Vec2 origin = Director::getInstance()->getVisibleOrigin();
	Vec2 s_centre = Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y);
	int dt = 50;
	/**
	Vec2 p = actionBody->getPosition();
	if (status == -1) {
		if (p.y <= dt) {
			status = 1;
			Director::getInstance()->getScheduler()->performFunctionInCocosThread([&]()
																				  {
																					  actionBody->setDynamic(false);
																					  actionBody->getNode()->setPosition(ballsposition);

																				  });
		}
	}
	 **/


	PhysicsBody *abody = contact.getShapeA()->getBody();
	PhysicsBody *bbody = contact.getShapeB()->getBody();
	PhysicsBody *findbody = nullptr;

	//看是否撞到篮板
	if (abody->getTag() == lgd->wallTag) {
		findbody = abody;
	}
	if (bbody->getTag() == lgd->wallTag) {
		findbody = bbody;
	}
	if (findbody != nullptr) {
		lgd->addCollStatusFlags(2);
		return true;
	}

	//是否撞到篮筐了
	if (abody->getTag() == lgd->kuangTag) {
		findbody = abody;
	}
	if (bbody->getTag() == lgd->kuangTag) {
		findbody = bbody;
	}
	if (findbody != nullptr) {
		lgd->addCollStatusFlags(1);
		return true;
	}

	//是否进球了
	if (abody->getTag() == lgd->ropeTag) {
		findbody = abody;
	}
	if (bbody->getTag() == lgd->ropeTag) {
		findbody = bbody;
	}
    CCLOG("%.2f, %.2f", findbody->getPosition().x, findbody->getPosition().y);
	if (findbody != nullptr && actionBody->getPosition().y >= findbody->getPosition().y) {
		lgd->addCollStatusFlags(3);
		NotificationCenter::getInstance()->postNotification("FallIn", this);
		/**
		findbody->removeFromWorld();
		findbody->getNode()->removeFromParent();
		findbody->setTag(-1);
		//下一帧添加
		Director::getInstance()->getScheduler()->performFunctionInCocosThread([&]()
																			  {
																		   baskWallLayer->reAddFallView();
	   });
		**/
	}

	return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
	//Close the cocos2d-x game scene and quit the application
	Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
	exit(0);
#endif

	/*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() and exit(0) as given above,instead trigger a custom event created in RootViewController.mm as below*/

	//EventCustom customEndEvent("game_scene_close_event");
	//_eventDispatcher->dispatchEvent(&customEndEvent);


}

void HelloWorld::exchangePosition() {
    Director::getInstance()->getScheduler()->performFunctionInCocosThread([&]()
                                                                          {
                                                                              resetBasket();
                                                                          });
    

}

void HelloWorld::resetBasket()
{
    actionBody->setDynamic(false);
    
    LogicData *lgd = LogicData::getInstance();
    CCLOG("pos: %.2f, %.2f", lgd->lankuangPosition_c.x, lgd->lankuangPosition_c.y);
    
    Vec2 l_ball;
    Vec2 l_basket;
    if (lgd->lankuangPosition_c.x<m_size.width/2) {
        //篮筐在左，换到右边
        l_ball = Vec2(random(150, 175), random(375, 415));
        l_basket = Vec2(random(460, 480), random(675, 700));
    }else{
        //篮筐在右，换到左边
        l_ball = Vec2(random(460, 480), random(375, 415));
        l_basket = Vec2(random(150, 175), random(675, 700));
    }
    
    Vec2 tmp = lgd->lankuangPosition_c;
    lgd->ballsposition_c = l_ball;
    lgd->lankuangPosition_c = l_basket;
    
    ballBox->setPosition(tmp);
    ball->setPosition(tmp);
    ball->runAction(MoveTo::create(1, l_ball));
    ballBox->runAction(MoveTo::create(1, l_ball));
    
    if (UserDataManager::getInstance()->battleData->hollowBallNumber>-1) {
        ParticleSystemQuad *psq = ParticleSystemCacheUtil::getSingleInstance()->addOrGetParticleSystem("particles/h01.plist");
        psq->setPosition(ball->getContentSize()/2);
        psq->setAutoRemoveOnFinish(true);
        ball->addChild(psq);
    }
    
    baskWallLayer->mainNode->setPosition(l_basket);
    baskWallLayer->setOpacity(0);
    baskWallLayer->runAction(FadeTo::create(1, 255));
}

void HelloWorld::onFallIn(Ref* pSender) {
	CCLOG("进球了...，计算得分 .");
    bool isKongxin = false;
    bool isLanban = false;
    
	LogicData *lgd = LogicData::getInstance();
	lgd->hasFallIn = true;
	//1篮筐， 2篮板， 3进篮
	//lgd->printInfo();
	//是否高射球
	bool isHightFire = false;
	if (lgd->ballMaxHeight >= lgd->hightFireHeight) {
		isHightFire = true;
		CCLOG("高射球 .");
	}
    
	//这里进行得分的判断
	int *flags = lgd->collStatusFlags;
	
	if (flags[0] == 3) {
        //空心球
		CCLOG("空心球 .");
		lgd->resetFireBallStatus();
//        exchangePosition();
        isKongxin = true;
	}
    if(flags[0] == 2 && lgd->collStatusFlagsIndex == 2) {
        //篮板球。只撞到篮板然后进球的.2,3 组合
		CCLOG("篮板球 .");
		lgd->resetFireBallStatus();
//        exchangePosition();
        isLanban = true;
    }
    CCLOG("普通球");
    lgd->resetFireBallStatus();

    CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("audio/touzhong.mp3");
    UserDataManager::getInstance()->battleData->calculateTheScore(isKongxin, isHightFire, isLanban);
    lab_score->setString(StringUtils::format("%d", UserDataManager::getInstance()->battleData->score));
    
    this->runAction(Sequence::create(DelayTime::create(1),
                                     CallFunc::create([=](){
        exchangePosition();
    }),
                                     NULL));
}

void HelloWorld::onOut(Ref* pSender) {
    if (m_fireFailure) {
        return;
    }
	LogicData *lgd = LogicData::getInstance();
	CCLOG("球飞出界面，可能会先调用onFallIn在进来这里. 是否进球： %d", lgd->hasFallIn);
	lgd->printInfo();
	lgd->resetFireBallStatus();
//    exchangePosition();
    
    m_fireFailure = true;
    
    if (UserDataManager::getInstance()->battleData->score<UserDataManager::getInstance()->userData->rewardList[1]) {
        auto l_failureView = FailureLayer::create();
        this->addChild(l_failureView);
    }else{
        auto winnerView = WinnerLayer::create();
        this->addChild(winnerView);
    }
}
